package com.ziyueyuwen.xiniao.base;

import com.suyeer.basic.util.HttpResUtil;
import com.suyeer.basic.util.LogUtil;
import com.suyeer.cache.RedisUtil;
import com.ziyueyuwen.xiniao.bean.ZyUser;
import com.ziyueyuwen.xiniao.bean.wx.TbWxUserInfo;
import com.ziyueyuwen.xiniao.enums.HttpStatusEnum;
import com.ziyueyuwen.xiniao.module.AuthInfoCache;
import com.ziyueyuwen.xiniao.util.ConstUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpStatus;
import org.apache.log4j.MDC;
import org.springframework.web.bind.MissingServletRequestParameterException;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.HashSet;
import java.util.regex.Pattern;

import static com.suyeer.basic.util.ConstUtil.GET;
import static com.ziyueyuwen.xiniao.base.BaseUtil.saveErrorMsgAndDataThenResponse;
import static com.ziyueyuwen.xiniao.util.ConstUtil.DATABASE_FOR_UPDATE_USER;
import static com.ziyueyuwen.xiniao.util.ConstUtil.KEY_UPDATE_USER_INFO;

/**
 * @author jun 2018/12/4
 */
public class AuthFilter implements Filter {

    private final HashSet<String> PASS_URI_SET = AuthInfoCache.getInstance().getWhiteUri();
    private final Pattern PATTERN = Pattern.compile("^/.*\\.(txt|ico|js|css|jpg|png|swf|svg|map)$");

    /**
     * 实现拦截器
     *
     * @param servletRequest  ServletRequest
     * @param servletResponse ServletResponse
     * @param chain           FilterChain
     * @throws IOException      IOException
     * @throws ServletException ServletException
     */
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        HttpResContent<String> hrc = new HttpResContent<>(request, response);
        initMDC(hrc);
        try {
            checkUserInfo(hrc);

            if (isPass(hrc.getReqURI()) && hrc.isPermission()) {
                chain.doFilter(request, response);
            } else if (hrc.isNotLogin() || hrc.isNotPermission()) {
                sendError(hrc);
            } else {
                chain.doFilter(request, response);
            }
        } catch (Exception e) {
            saveErrorMsgAndDataThenResponse(hrc, e);
        } finally {
            if (HttpStatus.SC_BAD_REQUEST == response.getStatus()) {
                MissingServletRequestParameterException exception = (MissingServletRequestParameterException) request.getAttribute("org.springframework.web.servlet.DispatcherServlet.EXCEPTION");
                LogUtil.error("请求缺少参数: {}", exception.getMessage());
            }
            MDC.clear();
        }
    }

    /**
     * 检查用户数据是否更新, 若更新后, 需要强制用户退出
     *
     * @param hrc HttpResContent<String>
     */
    private void checkUserInfo(HttpResContent<String> hrc) {
        try {
            if (hrc.isLogin()) {
                String userId = hrc.getUserInfo().getUserAccount();
                Boolean ifUpdate = RedisUtil.hasKey(DATABASE_FOR_UPDATE_USER, KEY_UPDATE_USER_INFO + userId);
                if (ifUpdate) {
                    RedisUtil.del(DATABASE_FOR_UPDATE_USER, KEY_UPDATE_USER_INFO + userId);
                    hrc.setUserSession(null);
                    hrc.setWxUserSession(null);
                }
            }
        } catch (Exception e) {
            LogUtil.error(e);
        }
    }

    /**
     * 初始化MDC
     *
     * @param hrc HttpResContent<String>
     */
    private void initMDC(HttpResContent<String> hrc) {
        try {
            ZyUser tbUserInfo = hrc.getUserInfo();
            TbWxUserInfo tbWxUserInfo = hrc.getWxUserInfo();
            if (tbUserInfo != null) {
                MDC.put("userId", tbUserInfo.getUserAccount());
                MDC.put("userName", (tbUserInfo.getUserName()));
            }
            if (tbWxUserInfo != null) {
                MDC.put("wxUserId", tbWxUserInfo.getWxUserId());
                MDC.put("wxNickname", tbWxUserInfo.getNickname());
            }
            MDC.put("userIp", hrc.getUserIp());
            MDC.put("referrer", StringUtils.substring(hrc.getReferrer(), 0, 990));
            MDC.put("userAgent", hrc.getUserAgent());
            MDC.put("reqQueryUri", hrc.getReqQueryURI());
            MDC.put("reqParams", hrc.getReqParams());
            MDC.put("sessionId", hrc.getSessionId());
        } catch (Exception e) {
            LogUtil.error(e);
        }
    }

    /**
     * 返回错误页面或错误提示
     *
     * @param hrc HttpResContent<String>
     * @throws Exception Exception
     */
    private void sendError(HttpResContent<String> hrc) throws Exception {
        HttpStatusEnum codeEnum = hrc.getHttpStatus();
        LogUtil.error(codeEnum.getDes());
        if (GET.equalsIgnoreCase(hrc.getReqMethod())) {
            if (codeEnum == HttpStatusEnum.UNAUTHORIZED) {
                hrc.getResponse().sendRedirect("/?backUrl=" + URLEncoder.encode(hrc.getReqQueryURI(), ConstUtil.DEFAULT_ENCODE));
            } else {
                hrc.getResponse().sendError(codeEnum.getCode(), codeEnum.getDes());
            }
        } else {
            hrc.fillData(codeEnum.getCode(), codeEnum.getDes(), null);
            HttpResUtil.writeResult(hrc, true);
        }
    }

    /**
     * 静态资源uri和免登陆uri返回true
     *
     * @param uri String
     * @return Boolean
     */
    private Boolean isPass(String uri) {
        return PATTERN.matcher(uri).matches() || PASS_URI_SET.contains(uri)
                || uri.startsWith("/test") || uri.startsWith("/")
                || uri.startsWith("/wx") || uri.endsWith(".p.do");
    }

    @Override
    public void destroy() {
        LogUtil.info("系统下线...");
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        LogUtil.info("系统部署成功...");
    }
}